#include <iostream>
#include <string>
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <functional>
#include <memory>
#include <unistd.h>

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void ()>;
class TimerTask
{
    private:
        uint64_t _id;       //定时器任务对象ID,可以唯一性的定位找到定时器对象
        uint32_t _timeout;  //设置定时任务的超时时间
        bool _canceled;     //false--表示没有取消(执行),true--表示取消(不执行)
        TaskFunc _task_cb;  //定时器对象要执行的定时任务,组件自身不需要知道参数,组件使用者会使用 bind 函数绑定任务传进来,你只负责执行
        ReleaseFunc _release; //用于删除TimerWheel中保存的定时器对象信息
    public:
        TimerTask(uint64_t id,uint32_t timeout,const TaskFunc& cb)
        : _id(id), _timeout(timeout), _task_cb(cb),_canceled(false)
        {}
        ~TimerTask(){
             if(_canceled == false) 
                _task_cb(); 
             _release();
            }
        void SetRelease(const ReleaseFunc& cb) {_release = cb;}
        void Cancel() {_canceled = true;}
        uint32_t Get_TimeOut() {return _timeout;}
};

class TimerWheel
{
    private:
        using WeakTask = std::weak_ptr<TimerTask>;
        using PtrTask = std::shared_ptr<TimerTask>;//我们使用它的引用计数,
        int _tick;           //代表秒针,走到那个位置就释放那个位置,释放的位置我会存入 weak指针指向的shared_ptr指针指向的定时器对象
                             //在释放的时候会调用 TimerTask 的析构函数,就会自动调用 定时器任务对象 所对应的定时任务(由用户传入)
        int _capacity;       //代表表盘最大容量---其实就是最大延迟时间,你可以自己设置为30s or 60s等等
        std::vector<std::vector<PtrTask>> _wheel;
        std::unordered_map<uint64_t,WeakTask> _timers;  //把 TimerTask对象以weak_ptr的形式存起来,方便后边查找并制作shared_ptr
                                                        //管理所有的定时器任务对象
    private:
        void RemoveTimer(const uint64_t id)
        {
            //先在 _timers中查找下有没有
            auto it = _timers.find(id);
            if(it != _timers.end())//找到啦
            {
                //将这个对象移除
                _timers.erase(id);
            }
        }
    public:
        TimerWheel()
        :_capacity(60),_tick(0),_wheel(_capacity) {}

        //添加定时任务
        void TimerAdd(uint64_t id,uint32_t timeout,const TaskFunc& cb){
            //先构建定时任务对象传递给shared_ptr
            PtrTask pt(new TimerTask(id,timeout,cb));
            pt->SetRelease(std::bind(&TimerWheel::RemoveTimer,this,id));
            //添加到 _timers 里
            _timers[id] = WeakTask(pt);
            //将 shared_ptr 放到 _wheel中
            uint32_t delay = pt->Get_TimeOut();
            int pos = (_tick + delay) % _capacity;//环形结构,越界后取模得到应该放入的位置
            _wheel[pos].push_back(pt);
        }
        //取消定时任务
        void TimerCancel(const uint64_t id)
        {
            auto it = _timers.find(id);
            if(it == _timers.end())
            {
                //说明没找到定时任务,没法刷新没法延迟,直接返回;
                return;
            }
            PtrTask pt = it->second.lock();//lock函数获取 weak_ptr 管理的对象对应的shared_ptr
            if(pt) pt->Cancel();
        }
        //延迟定时任务
        void TimerRefresh(uint64_t id)
        {
            //思路:先通过保存的定时器对象的 weak_ptr 构造出来一个shared_ptr出来,然后添加到时间轮中
            auto it = _timers.find(id);
            if(it == _timers.end())
            {
                //说明没找到定时任务,没法刷新没法延迟,直接返回;
                return;
            }
            //找到了定时任务
            //1.先通过保存的定时器对象的 weak_ptr 构造出来一个shared_ptr出来
            PtrTask pt = it->second.lock();//lock函数获取 weak_ptr 管理的对象对应的shared_ptr
            //2.添加到时间轮中
            uint32_t delay = pt->Get_TimeOut();
            int pos = (_tick + delay) % _capacity;//环形结构,越界后取模得到应该放入的位置
            _wheel[pos].push_back(pt);
        } 
        //秒针滴答向后走,走到哪里执行哪里的任务,这个函数每秒被执行一次,相当于秒针向后走了一步                              
        void RunTimerTask(){
            _tick = (_tick + 1) % _capacity;
            //清空指定位置的数组vector<PtrTask>,就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
            //假如如果有一个shared_ptr计数器为1,释放掉为0,这时就要去释放定时器对象,执行析构->执行任务
            _wheel[_tick].clear();
        }

};

//编写一个简单的测试类,通过它的构造和析构,看对象的释放过程,来看任务执行过程
class Test
{
    public:
        Test() {std::cout << "构造" << std::endl;}
        ~Test() {std::cout << "析构" << std::endl;}
};

void DelTest(Test* t)
{
    delete t;
}

int main ()
{
    TimerWheel tw;
    Test* t = new Test();
    tw.TimerAdd(666,5,std::bind(DelTest,t));
    for(int i = 0;i < 5;i++)
    {
        sleep(1);
        tw.TimerRefresh(666);
        tw.RunTimerTask();//秒针向后移动
        std::cout << "刷新定时任务,需要延迟5s才会销毁对象并执行任务" << std::endl;
    }
    tw.TimerCancel(666);
    while(1){
        sleep(1);
        std::cout << "----------------" << std::endl;
        tw.RunTimerTask();
    }
    return 0;
}